package cnet

import (
	"fmt"
	"github.com/cmajor/cherry/iface"
	"github.com/cmajor/cherry/utils"
	"net"
	"sync/atomic"
)

//Connection只负责 接受&发送&解析 网络数据包,这里不涉及业务
//关闭方式
//1 在读取时候出错 关闭
//2 在发送时候出错 关闭
//3 主动关闭
type TCPConn struct {
	conn      *net.TCPConn
	id        uint32
	closeFlag uint32 //0 表示正常 1 表示关闭

	parser  iface.IParser
	netRead iface.IConnReader

	delivery chan<- *iface.InternalMsg //将解析出来的消息发送出去处理

	writeChan chan []byte //将将要发送的数据放入管道,等待写协程去处理

	customObj interface{} //自定义数据
}

func NewTCPConn(conn *net.TCPConn, ID uint32, del chan<- *iface.InternalMsg) *TCPConn {
	c := &TCPConn{
		conn:      conn,
		id:        ID,
		closeFlag: 0,
		delivery:  del,
		writeChan: make(chan []byte, 100),
		parser:&BaseParser{},
		netRead:&ConnReader{},
	}

	go c.doWriter()
	return c
}

func (c *TCPConn) Run() {
	c.printLog("Conn Start()...")
	//c.parser = &BaseParser{}
	c.doReader()
}

func (c *TCPConn) Stop() {
	c.printLog("Conn Stop().. ")

	if c.IsClose() {
		return
	}
	c.Close()

	//向发送管道发送空数据 让发送协程跳出循环
	c.writeChan <- nil
	_ = c.conn.SetLinger(0)
	_ = c.conn.Close()

	close(c.writeChan)
}

func (c *TCPConn) ConnID() uint32 {
	return c.id
}

func (c *TCPConn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

func (c *TCPConn) doWriter() {
	c.printLog("[doWriter Goroutine is running]")
	defer func() {
		c.printLog(c.RemoteAddr().String(), "[doWriter Goroutine exit!]")
		c.Stop()
	}()
	for b := range c.writeChan {
		if b == nil {
			c.printLog("conn doWriter data == nil")
			return
		}
		if _, err := c.conn.Write(b); err != nil {
			c.printLog("conn doWriter Write error = ", err)
			return
		}
	}
}

func (c *TCPConn) doReader() {
	c.printLog("[doReader Goroutine is running...]")
	defer func() {
		c.printLog("[doReader Goroutine exit!]")
		c.Stop()
	}()

	readBuf := utils.NewCherryBuffer(utils.MaxPackageSize())
	for {
		if c.IsClose() {
			c.printLog("startReader isClosed return")
			return
		}

		if c.netRead == nil {
			panic("TCPConn reader == nil")
		}

		idata, err := c.netRead.Read(c.conn, readBuf)
		if err != nil {
			c.printLog("")
			return
		}

		msg := idata.(*iface.InternalMsg)
		msg.ConnID = c.id

		if c.parser != nil {
			t, err := c.parser.UnMarshal(msg.Content)
			if err != nil {
				fmt.Println("Read msg unmarshal error = ", err)
				continue
			}
			msg.CustomData = t
		}

		if c.delivery != nil {
			c.delivery <- msg
		}
	}
}

func (c *TCPConn) IsClose() bool {
	return atomic.LoadUint32(&c.closeFlag) > 0
}

func (c *TCPConn) Close() {
	atomic.SwapUint32(&c.closeFlag, 1)
}

func (c *TCPConn) SendCustom(msg interface{}) {
	if c.IsClose() {
		c.printLog("Connection closed")
	}

	bData, err := c.parser.Marshal(msg)
	if err != nil {
		c.printLog(" parser error = ", err)
		return
	}

	c.SendBytes(bData)
}

func (c *TCPConn) SendBytes(b []byte) {
	if c.IsClose() || b == nil {
		return
	}

	if len(c.writeChan) == cap(c.writeChan) {
		c.printLog("len(c.writeChan) == cap(c.writeChan) close connection")
		c.Stop()
		return
	}
	c.writeChan <- b
}

func (c *TCPConn) printLog(a ...interface{}) {
	fmt.Println("[connection id = ", c.ConnID(), "]", a)
}
